package cn.lh.util;

import org.apache.commons.io.FileUtils;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @描述:
 * @版权: Copyright (c) 2023
 * @作者: linhong
 * @版本: 1.0.0
 * @创建时间: 2023/12/29
 */
public class RSAKeyGeneratorUtil {

    //算法
    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
    public static final String PUBLIC_NAME = "public.txt";
    public static final String PRIVATE_NAME = "private.txt";

    public static final String ALGORITHM = "RSA";


    /**
     * 迭代次数
     */
    private static final int ITERATIONS = 65536;

    /**
     * 密钥长度
     */
    private static final int KEY_LENGTH = 256;

    //设置目录
    public static final String resourcesPath = System.getProperty("user.dir")+ File.separator+"spring-boot-demo"+ File.separator+"src"+File.separator+"main"
            +File.separator+"resources"+File.separator+"file"+File.separator;


    public static void main(String[] args) throws Exception {
//        String publicPath = resourcesPath + PUBLIC_NAME;
//        String privatePath = resourcesPath + PRIVATE_NAME;
//        generateKeyFile(ALGORITHM, publicPath, privatePath);


        String str = "qweasd123";
        String encryptStr = RSAEncrypt(str, null);
        System.err.println(encryptStr);

        String decryptStr = RSADecrypt(encryptStr, null);
        System.err.println(decryptStr);

        String s = hashPassword(decryptStr);
        System.err.println("hash:"+s);

    }

    /**
     * @描述: 生成公私钥
     * @版权: Copyright (c): 2023
     * @作者: linhong
     * @版本: 1.0.0
     * @创建时间: 2023/12/29
     * @param algorithm
    */
    private static void generateKeyFile(String algorithm,String publicPath, String privatePath) throws Exception {
        // 生成RSA密钥对
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);

        keyPairGenerator.initialize(512);
        //生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 获取公钥和私钥
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        byte[] privateKeyEncoded = privateKey.getEncoded();
        byte[] publicKeyEncoded = publicKey.getEncoded();

        String publicEncodeString = Base64.encodeBase64String(publicKeyEncoded);
        String privateEncodeString = Base64.encodeBase64String(privateKeyEncoded);


        // 打印公钥和私钥
        System.out.println("公钥：" + publicEncodeString);
        System.out.println("私钥：" + privateEncodeString);

        //需导入commons-io
        FileUtils.writeStringToFile(new File(publicPath), publicEncodeString, StandardCharsets.UTF_8);
        FileUtils.writeStringToFile(new File(privatePath), privateEncodeString, StandardCharsets.UTF_8);
    }



    /**
     * 加密
     * @param text      明文
     * @param key       私钥/公钥
     * @return 密文
     */
    public static String RSAEncrypt(String text,  Key key) throws Exception{
        if(key == null){
            key = getPublicKey(ALGORITHM, resourcesPath + PUBLIC_NAME);
        }
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] bytes = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
        return Base64.encodeBase64String(bytes);
    }


    public static String hashPassword(String passWord) {
        String generatedPassword = null;
        try{
            // Create messageDigest instance for MD5
            MessageDigest md = MessageDigest.getInstance("MD5");
            // Add password bytes to digest
            md.update(passWord.getBytes());
            // Get the hash's bytes
            byte[] digest = md.digest();
            // This bytes[] has butes in decimal format
            // Convert it to hexadecimal format
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < digest.length; i++){
                sb.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1));
            }
            generatedPassword = sb.toString();
        }catch(Exception e){
            e.printStackTrace();
        }
        return generatedPassword;
    }

    /**
     * 解密
     * @param extracted 密文
     * @param key       密钥/私钥
     * @return String 明文
     */
    public static  String RSADecrypt(String extracted, Key key) throws Exception{
        if(key == null){
            key = getPrivateKey(ALGORITHM, resourcesPath + PRIVATE_NAME);
        }
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] bytes = cipher.doFinal(Base64.decodeBase64(extracted));
        return new String(bytes);
    }


    /**
     * 获取公钥,key
     * @param algorithm  算法
     * @param publicPath 密匙文件路径
     * @return
     */
    private static PublicKey getPublicKey(String algorithm, String publicPath) throws Exception{
        String publicEncodeString = FileUtils.readFileToString(new File(publicPath), StandardCharsets.UTF_8);
        //返回转换指定算法的 public/private 关键字的 KeyFactory 对象。
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        //此类表示根据 ASN.1 类型 SubjectPublicKeyInfo 进行编码的公用密钥的 ASN.1 编码
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicEncodeString));
        return keyFactory.generatePublic(x509EncodedKeySpec);
    }

    /**
     * 获取私钥，key
     * @param algorithm   算法
     * @param privatePath 密匙文件路径
     * @return
     */
    private static PrivateKey getPrivateKey(String algorithm, String privatePath) throws Exception{
        String privateEncodeString = FileUtils.readFileToString(new File(privatePath), StandardCharsets.UTF_8);
        //返回转换指定算法的 public/private 关键字的 KeyFactory 对象。
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        //创建私钥key的规则  此类表示按照 ASN.1 类型 PrivateKeyInfo 进行编码的专用密钥的 ASN.1 编码
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateEncodeString));
        //私钥对象
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }

}
